unit uniPowerUps;

interface

uses
  uniScreenItem,
  uniDefine,
  gl,
  Classes;

const
  WOBBLE_0 = 45;
  WOBBLE_1 = 75;

type

  PowerUp = class;

  { PowerUps }

  PowerUps_Type =
    (
    Shields,
    SuperShields,
    Repair,
    HeroAmmo00,
    HeroAmmo01,
    HeroAmmo02,
    NumPowerUps
    );

  PowerUps = class(TObject)
  public
    pwrUpRoot: PowerUp;
    currentPwrUp: PowerUp;
    pwrUpSize: array[0..Integer(NumPowerUps) - 1, 0..1] of Single;
    pwrUpColor: array[0..Integer(NumPowerUps) - 1, 0..3] of Single;

    tex: array[0..Integer(NumPowerUps) - 1] of GLuint;
    pwrTex: GLuint;

    speed: Single;
    _wobble_0: array[0..WOBBLE_0 - 1] of Single;
    _wobble_1: array[0..WOBBLE_1 - 1] of Single;

    activeCount: Integer;

    //game: Global;

    constructor Create;
    destructor Destroy; override;

    procedure addPowerUp(temp: PowerUp);

    procedure update;
    procedure drawGL;
    procedure clear;

    function getFirst: PowerUp;
    function getNext: PowerUp;
    procedure remove(temp: PowerUp);

    procedure loadTextures;
    procedure deleteTextures;
  end;

  { PowerUp }

  PowerUp = class(ScreenItem)
  public
    _type: PowerUps_Type;
    power: Single;

    back: PowerUp;
    next: PowerUp;

    constructor Create(t: PowerUps_Type; p, v: array of Single; pwr: Single = 1.0);
    destructor Destroy; override;

    function thisCount: Integer; {return count_this;}

    procedure seal;

  end;

var
  count_this: Integer = 0;

implementation

uses
  PNGLoader,
  uniConfig,
  uniGlobal;

{ PowerUps }

procedure PowerUps.addPowerUp(temp: PowerUp);
var
  first: PowerUp;
begin
  Inc(activeCount);
  temp.back := pwrUpRoot;
  temp.next := pwrUpRoot.next;
  if Boolean(pwrUpRoot.next) then
  begin
    first := pwrUpRoot.next;
    first.back := temp;
  end;
  pwrUpRoot.next := temp;
end;

procedure PowerUps.clear;
var
  cur, del: PowerUp;
begin
  currentPwrUp := nil;
  cur := pwrUpRoot.next;
  while Boolean(cur) do
  begin
    del := cur;
    cur := cur.next;
    _itemAdd.killScreenItem(del);
  end;
  pwrUpRoot.next := nil;
end;

constructor PowerUps.Create;
var
  i: Integer;
  p: array[0..2] of Single;
  v: array[0..2] of Single;
  twoPi: Double;
begin
  activeCount := 0;
  currentPwrUp := nil;
  for i := 0 to Integer(NumPowerUps) - 1 do
  begin
    pwrUpSize[i][0] := 0.6;
    pwrUpSize[i][1] := 0.6;
    tex[i] := 0;
  end;
  speed := uniGlobal.scrollSpeed * 0.8;

  loadTextures;
  pwrUpColor[Integer(Shields)][0] := 0.5;
  pwrUpColor[Integer(Shields)][1] := 0.7;
  pwrUpColor[Integer(Shields)][2] := 1.0;
  pwrUpColor[Integer(Shields)][3] := 1.0;
  pwrUpColor[Integer(SuperShields)][0] := 1.0;
  pwrUpColor[Integer(SuperShields)][1] := 0.6;
  pwrUpColor[Integer(SuperShields)][2] := 0.0;
  pwrUpColor[Integer(SuperShields)][3] := 1.0;
  pwrUpColor[Integer(Repair)][0] := 1.0;
  pwrUpColor[Integer(Repair)][1] := 0.1;
  pwrUpColor[Integer(Repair)][2] := 0.0;
  pwrUpColor[Integer(Repair)][3] := 1.0;
  pwrUpColor[Integer(HeroAmmo00)][0] := 1.0;
  pwrUpColor[Integer(HeroAmmo00)][1] := 0.8;
  pwrUpColor[Integer(HeroAmmo00)][2] := 0.5;
  pwrUpColor[Integer(HeroAmmo00)][3] := 0.8;
  pwrUpColor[Integer(HeroAmmo01)][0] := 0.0;
  pwrUpColor[Integer(HeroAmmo01)][1] := 1.0;
  pwrUpColor[Integer(HeroAmmo01)][2] := 0.5;
  pwrUpColor[Integer(HeroAmmo01)][3] := 0.8;
  pwrUpColor[Integer(HeroAmmo02)][0] := 0.4;
  pwrUpColor[Integer(HeroAmmo02)][1] := 0.2;
  pwrUpColor[Integer(HeroAmmo02)][2] := 1.0;
  pwrUpColor[Integer(HeroAmmo02)][3] := 1.0;

  p[0] := 0.0;
  p[1] := 0.0;
  p[2] := 0.0;

  v[0] := 0.0;
  v[1] := 0.0;
  v[2] := 0.0;
  pwrUpRoot := PowerUp.Create(PowerUps_Type(-1), p, v);

  twoPi := 2.0 * M_PI;
  for i := 0 to WOBBLE_0 - 1 do
  begin
    _wobble_0[i] := 0.1 * Sin(twoPi * (i / WOBBLE_0));
  end;
  for i := 0 to WOBBLE_1 - 1 do
  begin
    _wobble_1[i] := 0.3 * Sin(twoPi * (i / WOBBLE_1));
  end;
end;

procedure PowerUps.deleteTextures;
var
  i: Integer;
begin
  for i := 0 to Integer(NumPowerUps) - 1 do
  begin
    glDeleteTextures(1, @tex[i]);
  end;
  glDeleteTextures(1, @pwrTex);
end;

destructor PowerUps.Destroy;
begin
  deleteTextures;
  clear;
  pwrUpRoot.Destroy;
  inherited;
end;

procedure PowerUps.drawGL;
var
  pos, sz: array of Single;
  szp: Single;
  pwrUp: PowerUp;
begin
  pwrUp := pwrUpRoot.next;
  while Boolean(pwrUp) do
  begin
    pos := @pwrUp.pos;
    sz := @pwrUpSize[Integer(pwrUp._type)];
    szp := sz[0] * 2.5;

    glColor4fv(@pwrUpColor[Integer(pwrUp._type)]);
    glBindTexture(GL_TEXTURE_2D, pwrTex);
    glPushMatrix;
    glTranslatef(
      pos[0] + _wobble_0[pwrUp.age mod WOBBLE_0],
      pos[1] + _wobble_1[pwrUp.age mod WOBBLE_1],
      pos[2]);
    glRotatef(IRAND, 0.0, 0.0, 1.0);
    glBegin(GL_QUADS);
    glTexCoord2f(0.0, 0.0); glVertex3f(-szp, szp, 0.0);
    glTexCoord2f(0.0, 1.0); glVertex3f(-szp, -szp, 0.0);
    glTexCoord2f(1.0, 1.0); glVertex3f(szp, -szp, 0.0);
    glTexCoord2f(1.0, 0.0); glVertex3f(szp, szp, 0.0);
    glEnd;
    glPopMatrix;
    pwrUp := pwrUp.next; //ADVANCE
  end;

  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  pwrUp := pwrUpRoot.next;
  while Boolean(pwrUp) do
  begin
    pos := @pwrUp.pos;
    sz := @pwrUpSize[Integer(pwrUp._type)];
    glColor4f(1.0, 1.0, 1.0, 1.0);
    glBindTexture(GL_TEXTURE_2D, tex[Integer(pwrUp._type)]);
    glPushMatrix;
    glTranslatef(
      pos[0] + _wobble_0[pwrUp.age mod WOBBLE_0],
      pos[1] + _wobble_1[pwrUp.age mod WOBBLE_1],
      pos[2]);
    glBegin(GL_QUADS);
    glTexCoord2f(0.0, 0.0); glVertex3f(-sz[0], sz[1], 0.0);
    glTexCoord2f(0.0, 1.0); glVertex3f(-sz[0], -sz[1], 0.0);
    glTexCoord2f(1.0, 1.0); glVertex3f(sz[0], -sz[1], 0.0);
    glTexCoord2f(1.0, 0.0); glVertex3f(sz[0], sz[1], 0.0);
    glEnd;
    glPopMatrix;
    pwrUp := pwrUp.next; //ADVANCE
  end;
  glBlendFunc(GL_SRC_ALPHA, GL_ONE);
end;

function PowerUps.getFirst: PowerUp;
begin
  currentPwrUp := pwrUpRoot.next;
  Result := currentPwrUp;
end;

function PowerUps.getNext: PowerUp;
var
  retVal: PowerUp;
begin
  retVal := nil;
  if Boolean(currentPwrUp) then
  begin
    currentPwrUp := currentPwrUp.next;
    retVal := currentPwrUp;
  end;
  Result := retVal;
end;

procedure PowerUps.loadTextures;
begin
  LoadTexture('png/powerUpTex.png', pwrTex);
  LoadTexture('png/powerUpShield.png', tex[Integer(Shields)]);
  tex[Integer(SuperShields)] := tex[Integer(Shields)];
  tex[Integer(Repair)] := tex[Integer(Shields)];
  LoadTexture('png/powerUpAmmo.png', tex[Integer(HeroAmmo00)]);
  tex[Integer(HeroAmmo01)] := tex[Integer(HeroAmmo00)];
  tex[Integer(HeroAmmo02)] := tex[Integer(HeroAmmo00)];
end;

procedure PowerUps.remove(temp: PowerUp);
begin
  if Boolean(temp) then
  begin
    currentPwrUp := temp.back;
    temp.seal;
  end;
  _itemAdd.killScreenItem(temp);
  Dec(activeCount);
  Writeln('active power ups = ', activeCount);
end;

procedure PowerUps.update;
var
  _config: Config;
  pwrUp, delUp: PowerUp;
  s, b: Single;
begin
  _config := uniConfig.instance;
  pwrUp := pwrUpRoot.next;
  while Boolean(pwrUp) do
  begin
    Inc(pwrUp.age);
    pwrUp.pos[1] := pwrUp.pos[1] + (speed * uniGlobal.speedAdj);
    if Boolean(Round(pwrUp.vel[0])) or Boolean(Round(pwrUp.vel[1])) then
    begin
      s := (1.0 - uniGlobal.speedAdj) + (uniGlobal.speedAdj * 0.982);
      pwrUp.vel[0] := pwrUp.vel[0] * s;
      pwrUp.vel[1] := pwrUp.vel[1] * s;
      pwrUp.pos[0] := pwrUp.vel[0] * pwrUp.vel[0];
      pwrUp.pos[1] := pwrUp.vel[1] * pwrUp.vel[1];
      if pwrUp.vel[0] < 0.01 then pwrUp.vel[0] := 0.0;
      if pwrUp.vel[1] < 0.01 then pwrUp.vel[1] := 0.0;
    end;
    b := _config.screenBoundX() - 1.0;
    if pwrUp.pos[0] < -b then pwrUp.pos[0] := -b;
    if pwrUp.pos[0] > b then pwrUp.pos[0] := b;
    if pwrUp.pos[1] < -12 then
    begin
      if _gameMode = uniGlobal.Game then
        case pwrUp._type of
          SuperShields:
            begin
              _hero.addLife;
              _hero.addScore(2500.0);
            end;
          Shields, Repair:
            begin
              _hero.addScore(10000.0);
            end;
        else
          _hero.addScore(2500.0);
        end;
      delUp := pwrUp;
      pwrUp := pwrUp.next;
      remove(delUp);
    end
    else
    begin
      pwrUp := pwrUp.next;
    end;
  end;
end;

{ PowerUp }

constructor PowerUp.Create(t: PowerUps_Type; p, v: array of Single; pwr: Single);
begin
  inherited Create(uniScreenItem.ItemPowerUp);

  Inc(count_this);
  _type := t;
  power := pwr;
  age := 0;
  pos[0] := p[0];
  pos[1] := p[1];
  pos[2] := p[2];
  if (v[0] <> 0) and (v[1] <> 0) and (v[2] <> 0) then
  begin
    vel[0] := v[0];
    vel[1] := v[1];
    vel[2] := v[2];
  end
  else
  begin
    vel[0] := 0.0;
    vel[1] := 0.0;
    vel[2] := 0.0;
  end;
  back := nil;
  next := nil;
end;

destructor PowerUp.Destroy;
begin
  Dec(count_this);
  inherited;
end;

procedure PowerUp.seal;
begin
  if Boolean(back) then
  begin
    if Boolean(next) then
    begin
      back.next := next;
      next.back := back;
    end
    else
    begin
      back.next := nil;
    end;
  end
  else
  begin
    //fprintf(stderr, "?? PowerUp::seal()");
    Writeln('?? PowerUp::seal()');
  end;
end;

function PowerUp.thisCount: Integer;
begin
  Result := count_this;
end;

end.

